package server;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;

import centralRepository.CentralRepository;
import centralRepository.ServerObject;
import centralRepository.ServerObjectImpl;

public class Broadcaster implements Runnable {
	private static final int BUFFER_LENGTH=211;
	private List<byte[]> queue = null;
	private List<ServerObject> serverObjectList = null;
	private List<ServerObject> alreadyCompleted = null;
	byte[] inBuf=new byte[BUFFER_LENGTH];
	private int leaderPortNumber;
	private ServerObjectImpl serverObject = null;
	private DatagramSocket server = null;

	public Broadcaster(ServerObjectImpl serverObject){
		this.serverObject = serverObject;
		queue = new ArrayList<byte[]>();
		serverObjectList = new ArrayList<ServerObject>();
		alreadyCompleted = new ArrayList<ServerObject>();
		try {
			if(serverObject.getName().toUpperCase().substring(0, 3).equals("SPL"))
				server = new DatagramSocket(7777);
			else if(serverObject.getName().toUpperCase().substring(0, 3).equals("SPB"))
				server = new DatagramSocket(8777);
			else if(serverObject.getName().toUpperCase().substring(0, 4).equals("SPVM"))
				server = new DatagramSocket(9777);
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public synchronized void addToQueue(byte[] message){
		queue.add(message);
		this.transmit();
	}

	public List<ServerObject> getServerObjectList() {
		return serverObjectList;
	}

	public void setServerObjectList(List<ServerObject> serverObjectList) {
		this.serverObjectList = serverObjectList;
	}

	public int getLeaderPortNumber() {
		return leaderPortNumber;
	}

	public void setLeaderPortNumber(String leaderPortNumber) {
		this.leaderPortNumber = Integer.parseInt(leaderPortNumber);
	}
	
	public void transmit(){
		if(!this.queue.isEmpty()){
			System.out.println("loaded queue");
			this.setServerObjectList(CentralRepository.getOtherRunningReplicas(serverObject));
			//get the first request in queue
			byte[] request = queue.get(0);
			try{
				server.setSoTimeout(20000);
				//broadcast to every replica
				for(int index = 0; index < this.serverObjectList.size(); index++){
					int port = Integer.parseInt(serverObjectList.get(index).getPortNum());
					InetAddress address = InetAddress.getByName(serverObjectList.get(index).getHostname());
					DatagramPacket outPacket;
					outPacket = new DatagramPacket(request, 0, request.length, address, port);
					if(!alreadyCompleted.isEmpty()){
						if(!alreadyCompleted.contains((serverObjectList).get(index))){
							//send request to replica
							server.send(outPacket);

							DatagramPacket inPacket = new DatagramPacket(inBuf, inBuf.length);
							//receive response from replica
							server.receive(inPacket);
							alreadyCompleted.add(serverObjectList.get(index));
						}
						else{
							//message has already been sent to this replica, ignore send
						}
					}
					else{
						//send request to replica
						server.send(outPacket);

						DatagramPacket inPacket = new DatagramPacket(inBuf, inBuf.length);
						//receive response from replica
						server.receive(inPacket);
						alreadyCompleted.add(serverObjectList.get(index));
					}
				}
				//request is complete, remove it from queue
				queue.remove(0);
				alreadyCompleted = new ArrayList<ServerObject>();
			} catch(SocketTimeoutException e) {
				System.out.println("Timed out broadcasting");
				transmit();
			} catch(Exception e) {
				e.printStackTrace();
			}
		}	
	}

	@Override
	public void run() {
		while(true) {
			if(!this.queue.isEmpty()){
				System.out.println("loaded queue");
				this.setServerObjectList(CentralRepository.getOtherRunningReplicas(serverObject));
				//get the first request in queue
				byte[] request = queue.get(0);
				try{
					DatagramSocket server = new DatagramSocket(this.leaderPortNumber);
					server.setSoTimeout(5000);
					//broadcast to every replica
					for(int index = 0; index < this.serverObjectList.size(); index++){
						int port = Integer.parseInt(serverObjectList.get(index).getPortNum());
						InetAddress address = InetAddress.getByName(serverObjectList.get(index).getHostname());
						DatagramPacket outPacket;
						outPacket = new DatagramPacket(request, 0, request.length, address, port);
						if(!alreadyCompleted.isEmpty()){
							if(!alreadyCompleted.contains((serverObjectList).get(index))){
								//send request to replica
								server.send(outPacket);

								DatagramPacket inPacket = new DatagramPacket(inBuf, inBuf.length);
								//receive response from replica
								server.receive(inPacket);
								alreadyCompleted.add(serverObjectList.get(index));
							}
							else{
								//message has already been sent to this replica, ignore send
							}
						}
						else{
							//send request to replica
							server.send(outPacket);

							DatagramPacket inPacket = new DatagramPacket(inBuf, inBuf.length);
							//receive response from replica
							server.receive(inPacket);
							alreadyCompleted.add(serverObjectList.get(index));
						}
					}
					//request is complete, remove it from queue
					queue.remove(0);
					alreadyCompleted = new ArrayList<ServerObject>();
					server.close();
				} catch(SocketTimeoutException e) {
					System.out.println("Timed out broadcasting");
					run();
				} catch(Exception e) {
					e.printStackTrace();
				}
			}	
		}
	}
}